<template>
  <div class="main-container">
    <el-form ref="taskForm" :model="taskData" :rules="rules">
      <!-- 任务基本信息 -->
      <div class="data-show">
        <!-- 头部标题 -->
        <div class="data-actions">
          <h3>任务基本信息</h3>
        </div>
        <!-- 内容区 -->
        <div class="data-table">
          <!-- 通用布局行 -->
          <div class="common-row row-between">
            <!-- 配置项 - 任务名称 -->
            <div class="common-item item-5">
              <label class="condition-label">任务名称</label>
              <el-form-item class="condition-input" prop="name">
                <el-input type="text" placeholder="请输入任务名称" v-model="taskData.name" />
              </el-form-item>
            </div>
            <!-- 配置项 - 平台 -->
            <div class="common-item item-5">
              <label class="condition-label">任务类型</label>
              <el-form-item class="condition-input" prop="module">
                <el-select  v-model="taskData.module" clearable placeholder="请选择任务类型">
                  <el-option v-for="(item, index) in modules" :key="index" :label="item.label" :value="item.value"></el-option>
                </el-select>
              </el-form-item>
            </div>
          </div>
          <!-- 通用布局行 -->
          <div class="common-row">
            <div class="common-item item-10">
              <label class="condition-label">任务描述</label>
              <el-form-item class="condition-input" prop="description">
                <el-input v-model="taskData.description" maxlength="100" type="textarea" show-word-limit :rows="4" resize="none" placeholder="请输入任务描述" />
              </el-form-item>
            </div>
          </div>
          <!-- 文件上传 -->
          <div class="common">
            <div class="common-item item-10">
              <label class="condition-label">文件上传</label>
              <!-- 隐藏的文件选择框 -->
              <input :ref="bigFile.refFn('fileInput')" @change="selectFileChange" type="file" style="display:none" />
              <!-- 上传展示区域 -->
              <div class="condition-input condition-uploading">
                <h4 class="uploading-title">{{ bigFile.uploadText }}</h4>
                <!-- 选取文件部分 -->
                <div class="uploading default aniShowOpt" @click="clickSelect" v-show="bigFile.type === 1">
                </div>
                <!-- 预检部分 -->
                <div class="uploading preparation aniShowOpt" v-show="bigFile.type === 2">
                  <div class="loader"></div>
                </div>
                <!-- 上传中部分 -->
                <div class="uploading doing aniShowOpt" :ref="bigFile.refFn('percentageDom')" v-show="bigFile.type === 3">
                  <div class="loader"></div>
                </div>
                <!-- 上传成功 -->
                <div class="uploading success aniShowOpt" v-show="bigFile.type === 4"></div>
                <!-- 上传失败 -->
                <div class="uploading fail aniShowOpt" v-show="bigFile.type === 5" @click="handleUploadFail"></div>
              </div>
            </div>
          </div>
        </div>
      </div>
      <!-- 底部的按钮 -->
      <div class="btn-groups">
          <button type="button" class="btn btn-normal" @click="handleCancel">取消</button>
          <button :disabled="taskData.btnFlag" type="button" class="btn" @click="handleComfirm">执行</button>
        </div>
    </el-form>
  </div>
</template>
<script setup>
import useAppStore from '@/stores/app';
import SparkMD5 from 'spark-md5';
import { ref, onActivated } from 'vue';
import { useRouter } from 'vue-router';
import { API } from '@/utils/status.js';
import { ElMessage } from 'element-plus';
import * as uploadApi from '@/api/file';
import * as rule from '@/utils/rules';
import { createTask } from '@/api/steganography';
const rules = {
  name: rule.createRequired('任务名称不能为空'),
  module: rule.createRequired('请选择任务类型'),
  fileId: rule.createRequired('请上传文件')
}

const modules = API.steCommon.modules;

// ============> data <============
const appStore = useAppStore();
const router = useRouter();
const taskForm = ref(null);
const taskData = ref({
  btnFlag: false,
  name: '',
  module: null,
  description: '',
  fileId: null
});
const bigFile = ref({
  fileInput: null,
  percentageDom: null,
  // 选择的文件对象
  selectFile: null,
  fileChunks: [],
  percentage: 0,
  uploadText: '点击此处选取上传文件',
  // 默认-1 预检中-2 上传中-3 上传成功-4 上传失败-5
  type: 1,
  refFn(key){
    return function (dom){
      if (dom){
        bigFile.value[key] = dom;
      }
    }
  },
  result: {
    fileId: null,
    fileName: null,
    fileUrl: null
  }
});



// ============> methods <============
// 点击选择文件
function clickSelect(){
  if (bigFile.value.selectFile){
    ElMessage.warning('当前已选取文件！');
    return;
  }
  bigFile.value.fileInput.click()
}
// 选择文件发送预检
async function selectFileChange(e){
  console.log('选取的文件e.target.files' + e.target.files);
  let [file] = e.target.files;
  if (!checkFile(file)) return;
  bigFile.value.selectFile = file;
  bigFile.value.type = 2;
  bigFile.value.uploadText = '正在进行文件预检，请稍后';
  let fileMD5 = await getMD5();
  console.log('计算的MD5')
  console.log(fileMD5)
  uploadApi.bigFilePerpare({
    fileName: bigFile.value.selectFile.name,
    fileSize: bigFile.value.selectFile.size,
    contentType: bigFile.value.selectFile.name.slice(bigFile.value.selectFile.name.lastIndexOf('.')),
    md5: fileMD5
  })
    .then(res => {
      console.log('预检返回')
      console.log(res)
      bigFile.value.result.fileId = '' + res.fileId;
      bigFile.value.result.fileName = res.fileName;
      if (res.upload){
        // 已上传
        uploadedCallback(res);
      } else {
        // 未上传
        handleUpload(res);
      }
    })
    .catch(err => {
      console.log('文件预检拦截错误')
      console.log(err)
      let msg = err.msg || err.message || '文件预检请求失败，请稍后重试或联系管理员';
      ElMessage.error(msg);
      bigFile.value.selectFile = null;
      bigFile.value.fileInput.value = null;
      bigFile.value.type = 1;
      bigFile.value.uploadText = '点击此处选取上传文件';
    });
}
// 已上传回调
function uploadedCallback(data){
  bigFile.value.uploadText = `文件${data.fileName}已上传`;
  bigFile.value.result.fileId = data.fileId;
  bigFile.value.result.fileName = data.fileName;
  bigFile.value.result.fileUrl = data.fileUrl;
  bigFile.value.type = 4;
  taskData.value.fileId = bigFile.value.result.fileId;
}
// 未上传回调
function handleUpload(data){
  //  { fileId, uploadId, fileName, maxPartSize, parts: [{partNumber,uploadUrl}] }
  let { 
    maxPartSize, 
    parts 
  } = data;
  sliceFile(maxPartSize);
  let unChunkSize = 0,
      totalSize = +bigFile.value.selectFile.size;
  parts.forEach(item => {
    bigFile.value.fileChunks[item.partNumber - 1].isUpload = false;
    bigFile.value.fileChunks[item.partNumber - 1].url = item.uploadUrl;
    unChunkSize += 1 * bigFile.value.fileChunks[item.partNumber - 1].file.size;
  });
  console.log(unChunkSize)
  bigFile.value.percentage = (((totalSize - unChunkSize) / totalSize) * 100).toFixed(2);
  bigFile.value.uploadText = `文件上传中 ${bigFile.value.percentage}%`;
  bigFile.value.percentageDom.style.width = `${bigFile.value.percentage}%`;
  bigFile.value.type = 3;
  upload();
}
// 切片上传
async function upload(){
  let promiseArr = [];
  bigFile.value.fileChunks.forEach(chunk => {
    if (!chunk.isUpload){
      console.log('每个上传的块')
      console.log(chunk)
      let promiseItem = new Promise((resolve, reject) => {
        let xhr = new XMLHttpRequest();
        xhr.open('put', chunk.url, true);
        xhr.send(chunk.file);
        xhr.onload = function (){
          if (this.status >= 200 && this.status < 300 || this.status == 304){
            setPercentage(+chunk.file.size)
            resolve(this.response);
          } else {
            reject(this.responese);
          }
        }
        xhr.onerror = function (e){
          console.log('单片上传失败uploadOnError');
          console.log(e);
          reject('分片上传请求失败')
        }
      });
      promiseArr.push(promiseItem);
    }
  });
  Promise.all(promiseArr)
    .then(result => {
      console.log(result)
      mergeFile();
    })
    .catch(err => {
      console.log('文件切片上传失败')
      console.log(err)
      let message = err.message || err.msg || '切片传输失败';
      bigFile.value.type = 5;
      bigFile.value.uploadText = `文件${bigFile.value.result.fileName}上传失败，${message}`;
    })
}
// 合并文件请求
async function mergeFile(){
  try {
    let result = await uploadApi.mergeChunk({fileId: bigFile.value.result.fileId});
    bigFile.value.type = 4;
    bigFile.value.uploadText = `文件${bigFile.value.result.fileName}上传合并成功`;
    taskData.value.fileId = bigFile.value.result.fileId;
  } catch (err){
    let msg = err.msg || err.message || '请稍后重试或联系管理员';
    bigFile.value.type = 5;
    bigFile.value.uploadText = `文件${bigFile.value.result.fileName}上传合并失败，${msg}`;
  }
}
// 文件大小及类型校验
function checkFile(file){
  let fileSize = file.size,
      fileType = file.name.slice(file.name.lastIndexOf('.') + 1),
      checkRes = true;
  if (fileSize > API.upload.bigFileLimitSize){
    ElMessage.warning(`文件大小超出限制，不得超过${API.upload.bigFileLimitSize/(1024*1024*1024)}G`);
    checkRes = false;
  }
  if (!API.upload.bigFileLimitTypes.includes(fileType)){
    ElMessage.warning(`上传文件格式不正确，需为${API.upload.bigFileLimitTypes.join('/')}格式文件`);
    checkRes = false;
  }
  return checkRes;
}
// 计算文件md5
function getMD5(){
  return new Promise((resolve, reject) => {
    const fileReader = new FileReader();
    fileReader.onload = (e) => {
      const fileMD5 = SparkMD5.ArrayBuffer.hash(e.target.result);
      resolve(fileMD5);
    }
    fileReader.onerror = (err) => {
      console.log(err);
      ElMessage.error(err);
      reject(err);
    }
    console.log('计算md5中')
    console.log('bigFile的文件')
    console.log(bigFile.value)
    console.log(bigFile.value.selectFile)
    // fileReader.readAsArrayBuffer(bigFile.value.selectFile);
    let fileSliceApi = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
    let fileResult = fileSliceApi.call(bigFile.value.selectFile, 0, 1.5 * 1024 * 1024 * 1024)
    fileReader.readAsArrayBuffer(fileResult);
  });
}
// 计算设置上传dom百分比
function setPercentage(addCount){
  let totalSize = +bigFile.value.selectFile.size;
  // console.log(addCount);
  // console.log(totalSize);
  // console.log(((addCount / totalSize) * 100).toFixed(2))
  // console.log(bigFile.value.percentage)
  // console.log(((addCount / totalSize).toFixed(2)))
  console.log(1*(((addCount / totalSize) * 100).toFixed(2)))
  bigFile.value.percentage = (1*bigFile.value.percentage + 1*(((addCount / totalSize) * 100).toFixed(2))).toFixed(2);
  bigFile.value.percentageDom.style.width = `${bigFile.value.percentage}%`;
  bigFile.value.uploadText = `文件上传中 ${bigFile.value.percentage}%`;
}
// 切片文件并组装
function sliceFile(maxPartSize){
  let fileSliceApi = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
  let start = 0,
      partNumber = 1,
      end,
      totalSize = +bigFile.value.selectFile.size;
  while(start < bigFile.value.selectFile.size){
    end = Math.min(start + 1 * maxPartSize, totalSize);
    bigFile.value.fileChunks.push({ 
      partNumber, 
      isUpload: true, 
      file: fileSliceApi.call(bigFile.value.selectFile, start, end),
      url: null
    });
    start = end;
    partNumber++;
  }
  console.log('文件切片结果')
  console.log(bigFile.value.fileChunks);
}
// 处理上传失败
function handleUploadFail(){
  bigFile.value.fileInput.value = null;
  bigFile.value.selectFile = null;
  bigFile.value.fileChunks = [];
  bigFile.value.percentage = 0;
  bigFile.value.uploadText = '点击此处选取上传文件';
  bigFile.value.type = 1;
}
function handleCancel(){
  router.back(-1);
}
function handleComfirm(){
  if (taskData.value.btnFlag){
    ElMessage.warning('当前正在执行，请稍后');
    return;
  }
  if (!taskData.value.fileId){
    ElMessage.warning('请先上传文件');
    return;
  }
  taskForm.value.validate(valid => {
    if (valid){
      taskData.value.btnFlag = true;
      appStore.openLoading('分析任务创建中...');
      createTask({
        name: taskData.value.name,
        description: taskData.value.description,
        moduleType: API.steganography.moduleType.ANALYSIS,
        infoData: {
          module: taskData.value.module,
          fileId: taskData.value.fileId
        }
      })
        .then(res => {
          console.log(res)
          taskData.value.btnFlag = false;
          appStore.closeLoading();
          router.push({ name: 'analysis' });
        })
        .catch(err => {
          let msg = err.msg || err.message || '执行任务创建失败，请稍后重试或联系管理员';
          ElMessage.error(msg);
          taskData.value.btnFlag = false;
          appStore.closeLoading();
        })
    } else {
      ElMessage.warning('提交信息有误，请核对后提交');
    }
  })
}
</script>
<style lang='scss' scoped>
@import '@/assets/styles/mixin.scss';
// 文件上传块的样式
.condition-uploading {
  height: 90px;
  background: var(--color-main-2);
  border: 1px solid var(--color-main);
  border-radius: 6px;
  position: relative;
  overflow: hidden;
  &::v-deep .el-form-item__content{
    position: relative;
    height: 100%;
  }
  @include flex-all-center;
  .uploading-title {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 2;
    color: var(--color-white-10);
  }
  .uploading {
    position: absolute;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    @include flex-all-center;
    h4 {
      color: var(--color-white-10);
    }
    .loader {
      position: absolute;
      inset:  0 100% 0 0;
      background-color: var(--color-white-1);
      animation: l6 2s infinite;
    }
  }
  .default {
    background-color: var(--color-upload-default);
    cursor: pointer;
  }
  .preparation {
    background-color: var(--color-upload-preparation);
  }
  .doing {
    background-color: var(--color-upload-doing);
    width: 0%;
    transition: all 0.5s linear;
  }
  .success {
    background-color: var(--color-upload-success);
  }
  .fail {
    background-color: var(--color-upload-fail);
    cursor: pointer;
  }
}
@keyframes l6 {
  100% {inset:0}
}
.row-between {
  justify-content: space-between;
}
.item-3 {
  width: 30%
}
.item-5 {
  width: 49%;
  // height: auto;
  // padding: 50px 0 60px 0;
}
.item-10 {
  width: 100%;
  height: auto;
  padding: 10px 0;
  .condition-label {
    width: 15%;
  }
  .condition-input {
    width: 80%;
  }
}
.item-10-spe {
  width: 100%;
  .condition-label {
    width: 15%;
  }
  .condition-input {
    width: 80%;
  }
  &::v-deep .el-form-item__content {
    align-items: center!important;
  }
}
.data-title-between {
  @include flex-between-center;
  .data-title-actions {
    display: flex;
  }
}
.btn-groups {
  margin-top: 20px;
  text-align: center;
  .btn {
    margin: 0 20px;
  }
}
.item-upload {
  padding-bottom: 50px;
}
.upload {
  width: 100%;
  min-height: 120px;
  height: 120px;
  &::v-deep .el-upload {
    @include wh-100;
    // height: 100%!important;
    .el-upload-dragger {
      @include wh-100;
      border-color: var(--color-main);
      padding: 0;
      .upload-empty {
        @include wh-100;
        @include flex-all-center;
        flex-direction: column;
        .upload-icon {
          font-size: 30px;
          color: var(--color-gray-10);
        }
        .upload-des {
          color: var(--color-gray-10);
        }
      }
      .upload-content {
        @include wh-100;
        @include flex-all-center;
        flex-direction: column;
        .upload-icon {
          font-size: 30px;
          color: var(--color-main);
        }
        .upload-des {
          color: var(--color-main);
        }
      }
    }
  }
}
.condition-label-change {
  cursor: pointer;
}
.condition-label-change:hover {
  color: var(--color-primary);
}
.audio-container {
  width: 100%;
  height: 190px;
  background-color: var(--color-gray-2);
  border: 1px solid var(--color-main-border);
  border-radius: 6px;
  position: relative;
  overflow: hidden;
  @include flex-all-center;
  .audio-icon {
    font-size: 40px;
    font-weight: bolder;
    color: var(--color-main);
  }
  .audio {
    position: absolute;
    bottom: 0;
    left: 50%;
    width: 105%;
    height: 30px;
    transform: translateX(-50%);
  }
}
.data-table-scroll {
  max-height: 220px;
  height: auto;
  overflow-y: auto;
  @include scrollBar;
  .file-image {
    width: 100%;
    height: 190px;
    background-color: var(--color-gray-2);
    border: 1px solid var(--color-main-border);
    border-radius: 6px;
  }
}
</style>